package com.gitee.wsl.ext

import androidx.compose.runtime.withFrameNanos
import com.gitee.wsl.Lib
import com.gitee.wsl.base.Option
import com.gitee.wsl.base.empty
import com.gitee.wsl.concurrent.api.cancel.Cancellable
import com.gitee.wsl.ext.base.currentTimeMillis
import com.gitee.wsl.ext.base.trueValue
import com.gitee.wsl.ioDispatcher
import com.gitee.wsl.platform.concurrent.singleThreadDispatcher
import com.gitee.wsl.platform.concurrent.threadPoolDispatcher
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.MainCoroutineDispatcher
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import kotlin.coroutines.Continuation
import kotlin.coroutines.CoroutineContext
import kotlin.time.Duration
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.InternalForInheritanceCoroutinesApi
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.coroutines.coroutineContext
import kotlin.time.measureTime


const val NO_GETTER: String = "Property does not have a getter"

fun noGetter(): Nothing = throw NotImplementedError(NO_GETTER)

object K {

    /**
     * detail: 默认值常量
     * @author Ttt
     */
    object DEFAULT {
        const val ERROR_INT = -1
        const val ERROR_LONG = -1L
        const val ERROR_FLOAT = -1f
        const val ERROR_DOUBLE = -1.0
        const val ERROR_BOOLEAN = false
        const val ERROR_SHORT: Short = -1
        const val ERROR_CHAR: Char = (-1).toChar()
        const val ERROR_BYTE = ((-1).toByte())
        val ERROR_STRING: String? = null
        const val INT = 0
        const val LONG = 0L
        const val FLOAT = 0f
        const val DOUBLE = 0.0
        const val BOOLEAN = false
        const val SHORT: Short = 0
        const val CHAR = 0.toChar()
        const val BYTE = 0.toByte()
        const val STRING = ""
    }

    var defaultExceptionHandler: CoroutineExceptionHandler = CoroutineExceptionHandler {
            _, exception -> println("Caught $exception")
    }

    val MAIN: MainCoroutineDispatcher get() = Dispatchers.Main
    val IO: CoroutineDispatcher get() = Lib.ioDispatcher
    val Unconfined: CoroutineDispatcher get() = Dispatchers.Unconfined
    val Default: CoroutineDispatcher get() = Dispatchers.Default
    @OptIn(ExperimentalCoroutinesApi::class)
    val Single: CoroutineDispatcher by lazy { singleDispatcher("Single") }

    @OptIn(ExperimentalCoroutinesApi::class)
    fun singleDispatcher(name: String) = singleThreadDispatcher(name)

    @OptIn(ExperimentalCoroutinesApi::class)
    fun poolDispatcher(numberOfThreads: UInt, name: String) = threadPoolDispatcher(numberOfThreads, name)
}


fun <T> K.runBlocking(context: CoroutineContext = Dispatchers.Default, block: suspend CoroutineScope.() -> T): T
      = com.gitee.wsl.platform.concurrent.runBlocking(context, block)

fun <T> K.runOnMain( block: () -> Unit) = com.gitee.wsl.platform.concurrent.runOnMain (block)

suspend inline fun <T> K.supervisorScope(crossinline block: (Continuation<T>) -> Unit): T = kotlin.coroutines.suspendCoroutine(block)

suspend fun <R> K.coroutineScope(block: suspend CoroutineScope.() -> R): R = kotlinx.coroutines.coroutineScope(block)

suspend inline fun <T> K.suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T = kotlin.coroutines.suspendCoroutine(block)

suspend inline fun <T> K.suspendCancellableCoroutine(
    crossinline block: (CancellableContinuation<T>) -> Unit
): T = kotlinx.coroutines.suspendCancellableCoroutine(block)


inline fun K.runOnScope(
    context: CoroutineDispatcher = Dispatchers.Default,
    crossinline block: suspend CoroutineScope.() -> Unit
): Job = CoroutineScope(context).launch {
    block(this)
}

fun <T> K.runAsyncOnScope(
    context: CoroutineDispatcher = Dispatchers.Default,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T> = CoroutineScope(context).async(context, start, block)



suspend fun <T> K.withTimeout(timeMillis: Long, block: suspend CoroutineScope.() -> T): T = kotlinx.coroutines.withTimeout(timeMillis, block)

suspend fun <T> K.withTimeout(timeout: Duration, block: suspend CoroutineScope.() -> T): T = kotlinx.coroutines.withTimeout(timeout, block)

suspend fun <T> K.withTimeoutOrNull(timeMillis: Long, block: suspend CoroutineScope.() -> T): T? = kotlinx.coroutines.withTimeoutOrNull(timeMillis, block)

suspend fun <T> K.withTimeoutOrNull(timeout: Duration, block: suspend CoroutineScope.() -> T): T? = kotlinx.coroutines.withTimeoutOrNull(timeout, block)


suspend fun K.delay(ms: Long, block: suspend () -> Unit){
    kotlinx.coroutines.delay(ms)
    block()
}


inline fun <R> K.awaitUiResultWithPending(uiScope: CoroutineScope, crossinline action: (CancellableContinuation<R>) -> Unit): R {
    return runBlocking {
        kotlinx.coroutines.suspendCancellableCoroutine {
            uiScope.launch(Dispatchers.Main) { action(it) }
        }
    }
}

inline fun <R> K.awaitUiResult(uiScope: CoroutineScope, crossinline action: () -> R): R {
    return runBlocking {
        kotlinx.coroutines.suspendCancellableCoroutine {
            uiScope.launch(Dispatchers.Main) {
                it.resumeWith(Result.success(action()))
            }
        }
    }
}

inline fun <R> K.runCatching(block: () -> R): Result<R> = kotlin.runCatching(block)


// Interruptible 相关

/**
 * 示列
 *  withTimeout(500L) {            // Cancels coroutine on timeout
 *    runInterruptible {         // Throws CancellationException if interrupted
 *       doSomethingBlocking()  // Interrupted on coroutines cancellation
 *     }
 *  }
 */
/*suspend fun <T> runInterruptible(
    context: CoroutineContext = EmptyCoroutineContext,
    block: () -> T
): T = kotlinx.coroutines.runInterruptible(context, block)*/


suspend inline fun <reified T> K.runWithLock(lock: Mutex, dispatcher: CoroutineDispatcher = Dispatchers.Default, crossinline block: () -> T) = withContext(dispatcher) {
    lock.withLock { block() }
}

inline fun K.runOnCancelAble(cancelBlock: Cancellable, crossinline block: suspend Cancellable.() ->Unit){
    runOnScope {
        try {
            block(cancelBlock)
        } catch (e: Exception) {
            cancelBlock.cancel()
        }
    }
}

suspend fun <T> K.withNonCancellableContext(block: suspend CoroutineScope.() -> T) =
    withContext(NonCancellable, block)

/*
* 异步懒加载，by FunnySaltyFish
*
* @param T 要加载的数据类型
* @param scope 加载时的协程作用域
* @param block 加载代码跨
* @return Lazy<Deferred<T>>
*/
fun <T> K.lazyPromise(scope: CoroutineScope = MainScope(), block: suspend CoroutineScope.() -> T) =
    lazy {
        scope.async(start = CoroutineStart.LAZY) {
            block.invoke(this)
        }
    }

@OptIn(InternalForInheritanceCoroutinesApi::class)
fun <T> K.deferred(block: suspend () -> T): Deferred<T> {
    val deferred = CompletableDeferred<T>()
    return object : Deferred<T> by deferred {
        private val mutex = Mutex()
        override suspend fun await(): T {
            mutex.withLock {
                if (!deferred.isCompleted) {
                    block().also { deferred.complete(it) }
                }
            }
            return deferred.await()
        }
    }
}


inline fun <R> K.tryFinally(tryBlock: () -> R, finallyBlock: () -> Unit): R {
    var tryException: Exception? = null
    return try {
        tryBlock()
    } catch (e: Exception) {
        tryException = e
        throw tryException
    } finally {
        try {
            finallyBlock()
        } catch (finallyException: Exception) {
            if (tryException == null) throw finallyException
            tryException.addSuppressed(finallyException)
        }
    }
}


/**
 * Executes the given [action] until getting of successful result specified number of [times].
 *
 * A zero-based index of current iteration is passed as a parameter to [action].
 */
inline fun <R> K.repeatOnFailure(
    onFailure: (Throwable) -> Boolean,
    action: () -> R
): Result<R> {
    do {
        runCatching {
            action()
        }.onFailure {
            if (!onFailure(it)) {
                return Result.failure(it)
            }
        }.onSuccess {
            return Result.success(it)
        }
    } while (true)
}

/**
 * Executes the given [action] until getting of successful result specified number of [times].
 *
 * A zero-based index of current iteration is passed as a parameter to [action].
 */
inline fun <R> K.repeatOnFailure(
    times: Int,
    onEachFailure: (Throwable) -> Unit = {},
    action: (Int) -> R
): Option<R> {
    var i = 0
    val result = repeatOnFailure(
        {
            onEachFailure(it)
            if (i < times) {
                i++
                true
            } else {
                false
            }
        }
    ) {
        action(i)
    }
    return if (result.isSuccess) {
        Option(result.getOrThrow())
        //Option.presented(result.getOrThrow())
    } else {
        Option.empty<R>()
    }
}


fun <T> K.tryOrNull(block: () -> T): T? =
    try {
        block()
    } catch (e: Exception) {
        null
    }


fun <T> tryOrNull(block: () -> T): T? =
    try {
        block()
    } catch (e: Exception) {
        null
    }


inline fun <T> List<T>.runEachOnSemaphoreScope(
    semaphoreCount:Int = 5,
    context: CoroutineDispatcher = Dispatchers.Default,
    crossinline block: suspend CoroutineScope.(index:Int,item:T) -> Unit):Job{
    val barrier: Semaphore? = (size > semaphoreCount).trueValue(Semaphore(semaphoreCount))
    return K.runOnScope(context) {
        forEachIndexed { index, t ->
            launch(context) {
                try {
                    barrier?.acquire()
                    block(index, t)
                } finally {
                    barrier?.release()
                }
            }
        }
    }
}

inline fun <T> List<T>.runEachOnScope(
    context: CoroutineDispatcher = Dispatchers.Default,
    crossinline block: suspend CoroutineScope.(index:Int,item:T) -> Unit):Job {
    return K.runOnScope(context) {
        forEachIndexed { index, t ->
            launch(context) {
                block(index, t)
            }
        }
    }
}


suspend fun <T> K.retry(
    times: Int = 3,
    initialDelay: Long = 100, // 0.1 second
    maxDelay: Long = 1000,    // 1 second
    factor: Double = 2.0,
    block: suspend () -> T
): T {
    return withContext(Dispatchers.ioDispatcher) {
        var currentDelay = initialDelay
        repeat(times - 1) {
            try {
                return@withContext block()
            } catch (e: Exception) {
                // you can log an error here and/or make a more finer-grained
                // analysis of the cause to see if retry is needed
            }
            delay(currentDelay)
            currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay)
        }
        return@withContext block() // last attempt
    }
}

fun <T> K.retryBlocking(
    times: Int = 3,
    initialDelay: Long = 100, // 0.1 second
    maxDelay: Long = 1000,    // 1 second
    factor: Double = 2.0,
    block: suspend () -> T
): T = K.runBlocking { retry(times, initialDelay, maxDelay, factor, block) }


suspend fun K.gameLoop(
    onFrame: (timeNanos: Long, deltaNs: Long, delta: Double) -> Unit,
) {
    var previousTime = -1L
    while (currentCoroutineContext().isActive) {
        withFrameNanos { time ->
            if (previousTime == -1L) {
                previousTime = time
                return@withFrameNanos
            }

            val delta = time - previousTime
            val deltaDouble = (delta.toDouble() / 1E9)
            previousTime = time

            onFrame(time, delta, deltaDouble)
        }
    }
}

/**
 * Executes the given [block] and returns elapsed time in milliseconds.
 *
 * This function is obsolete, and it is recommended to use [measureTime] instead as
 * it does not suffer from [measureTimeMillis] downsides and provides human-readable output.
 *
 * [measureTimeMillis] uses `System.currentTimeMillis` which is not monotonic, is a subject
 * to a clock drift, and has an OS-dependent coarse-grained resolution.
 * [measureTimeMillis] can return a negative or zero value as a result.
 *
 * @see measureTime
 * @sample samples.system.Timing.measureBlockTimeMillis
 */
inline fun K.measureTimeMillis(block: () -> Unit): Long = measureTime(block).inWholeMilliseconds

inline fun K.measureTimeNanos(block: () -> Unit): Long = measureTime(block).inWholeNanoseconds

inline fun K.measureTime(block: () -> Unit) = kotlin.time.measureTime(block)